<?php
namespace PsiFramework\Classes\Util\Security\Manager;

import("Classes.Factory.DAOFactory");
import("Classes.Util.Log.Logger");
import("Classes.Util.HTTP.AuthenticationManager");

use \PsiFramework\Classes\Factory\DAOFactory;
use \PsiFramework\Classes\Model\SessionUser;
use \PsiFramework\Classes\Util\Application;
use \PsiFramework\Classes\Util\Log\Logger;
use \PsiFramework\Classes\Util\HTTP\AuthenticationManager;
use \PsiFramework\Classes\Util\HTTP\Request;
use \PsiFramework\Classes\Util\HTTP\Session;
use \PsiFramework\Classes\Util\Manager\ApplicationManager;

/**
 * PsiFramework
 * Php Simple Framework
 *
 * @author Rémi San
 * @version beta
 */

/**
 * Classe Manager pour les droits
 *
 * @package Classes.Util.Security.Manager
 */
class SecurityManager
{

    protected static $_singleton = null ;

    protected /*array*/ $_rights ;

    protected static /*Logger*/    $_logger ;

    protected function __construct(
        SessionUser $user=null,
        /*boolean*/ $securityCheck=true
    )
    {

        $this->_rights = array();

        $rightDao = DAOFactory::getDAO("SecurityRight");
        $securityRights = $rightDao->getRightsForSessionUser($user);
        if (!is_null($securityRights)) {

            foreach ($securityRights as $securityRight) {
                $application = $securityRight->getApplication();
                $action = $securityRight->getAction();
                $object = $securityRight->getObject();

                if (!array_key_exists($application, $this->_rights)) {
                    $this->_rights[$application]
                        = array(
                            'rights'=>array(
                                'readable'=>false,
                                'writable'=>false,
                                'executable'=>false
                            ),
                            'actions'=>array()
                        );
                }

                $appParams
                    = $this->_rights[$application]['actions'] ;
                if (!array_key_exists($action, $appParams)) {
                    $this->_rights[$application]['actions'][$action]
                        = array(
                            'rights'=>array(
                                'readable'=>false,
                                'writable'=>false,
                                'executable'=>false
                            ),
                            'objects'=>array()
                        );
                }

                $actionParams
                    = $this->_rights[$application]
                           ['actions'][$action]['objects'] ;
                if (!array_key_exists($object, $actionParams)) {
                    $this->_rights[$application]
                        ['actions'][$action]
                        ['objects'][$object]
                            = array(
                                'readable'=>false,
                                'writable'=>false,
                                'executable'=>false
                            );
                }

                if ($securityRight->isReadable()) {
                    if (isNullOrEmptyString($object)
                        || !isNullOrEmptyString($application)
                    ) {
                        $this->_rights[$application]
                               ['rights']['readable']=true ;
                    }
                    
                    if (isNullOrEmptyString($object)
                        || !isNullOrEmptyString($action)
                    ) {
                        $this->_rights[$application]
                               ['actions'][$action]
                               ['rights']['readable']=true ;
                    }
                    
                    $this->_rights[$application]
                           ['actions'][$action]
                           ['objects'][$object]['readable'] = true ;
                }
                if ($securityRight->isWritable()) {
                    if (isNullOrEmptyString($object)
                        || !isNullOrEmptyString($application)
                    ) {
                        $this->_rights[$application]
                               ['rights']['writable'] = true ;
                    }
                    
                    if (isNullOrEmptyString($object)
                        || !isNullOrEmptyString($action)
                    ) {
                        $this->_rights[$application]
                               ['actions'][$action]
                               ['rights']['writable'] = true ;
                    }
                    
                    $this->_rights[$application]
                           ['actions'][$action]
                           ['objects'][$object]['writable'] = true ;
                }
                if ($securityRight->isExecutable()) {
                    if (isNullOrEmptyString($object)
                        || !isNullOrEmptyString($application)
                    ) {
                        $this->_rights[$application]
                               ['rights']['executable'] = true ;
                    }
                    
                    if (isNullOrEmptyString($object)
                        || !isNullOrEmptyString($action)
                    ) {
                        $this->_rights[$application]
                               ['actions'][$action]
                               ['rights']['executable'] = true ;
                    }
                    
                    $this->_rights[$application]
                           ['actions'][$action]
                           ['objects'][$object]['executable'] = true ;
                }
            }
        }
    }

    public function canReadApplication(Application $app)
    {

        $appName = '' ;
        if (!is_null($app)) {
            if (!$app->securityCheck()) {
                return true ;
            }
            $appName = $app->getName();
        }

        $canRead = false ;

        if (array_key_exists('', $this->_rights)) {
            $rights = $this->_rights['']['rights'] ;
            $canRead = $rights['readable'] ||
                       $rights['writable'] ||
                       $rights['executable'] ;
        }

        if (!$canRead && !is_null($app)
            && array_key_exists($appName, $this->_rights)
        ) {
            $rights = $this->_rights[$app->getName()]['rights'] ;
            $canRead = $rights['readable'] ||
                       $rights['writable'] ||
                       $rights['executable'] ;
        }

        self::$_logger->debug(
            "Application (".$appName.") security check : " .
            (($canRead)?'true':'false')
        );

        return $canRead ;
    }

    public function canExecuteAction($actionName, Application $app)
    {

        $appName = '' ;
        if (!is_null($app)) {
            if (!$app->securityCheck()) {
                return true ;
            }
            $appName = $app->getName();
        }

        $canExecute = false ;

        if (array_key_exists('', $this->_rights)) {
            if (array_key_exists('', $this->_rights['']['actions'])) {
                $rights = $this->_rights['']['actions']['']['rights'] ;
                $canExecute = $rights['readable'] ||
                              $rights['writable'] ||
                              $rights['executable'] ;
            }
            if (!$canExecute
                && array_key_exists($actionName, $this->_rights['']['actions'])
            ) {
                $rights = $this->_rights['']
                                 ['actions'][$actionName]
                                 ['rights'] ;
                $canExecute = $rights['readable'] ||
                              $rights['writable'] ||
                              $rights['executable'] ;
            }
        }

        if (!$canExecute
            && !is_null($app)
            && array_key_exists($appName, $this->_rights)
        ) {
            $appName = $app->getName();
            if (array_key_exists('', $this->_rights[$appName]['actions'])) {
                $rights = $this->_rights[$appName]
                                       ['actions']['']['rights'] ;
                $canExecute = $rights['readable'] ||
                              $rights['writable'] ||
                              $rights['executable'] ;
            }
            if (!$canExecute
                && array_key_exists(
                    $actionName,
                    $this->_rights[$appName]['actions']
                )
            ) {
                $rights = $this->_rights[$appName]
                                 ['actions'][$actionName]
                                 ['rights'] ;
                $canExecute = $rights['readable'] ||
                              $rights['writable'] ||
                              $rights['executable'] ;
            }
        }

        self::$_logger->debug(
            "Action (".$actionName.") security check : " .
            (($canExecute)?'true':'false')
        );

        return $canExecute ;
    }

    public function canReadObject(
        $object='',
        $actionName='',
        Application $app=null
    )
    {
        if (is_null($app)) {
            return false ;
        }
        return $this->canAccessObject($object, $actionName, $app, 'readable');
    }

    public function canWriteObject(
        $object='',
        $actionName='',
        Application $app=null
    )
    {
        if (is_null($app)) {
            return false ;
        }
        return $this->canAccessObject($object, $actionName, $app, 'writable');
    }

    public function canAccessObject(
        $object='',
        $actionName='',
        Application $app=null,
        $rightName='readable'
    )
    {

        $appName = '' ;
        if (!is_null($app)) {
            if (!$app->securityCheck()) {
                return true ;
            }
            $appName = $app->getName();
        }

        $canExecute = false ;

        if (array_key_exists('', $this->_rights)) {
            if (array_key_exists(
                '',
                $this->_rights['']['actions']
            )
            ) {
                if (array_key_exists(
                    '',
                    $this->_rights['']
                    ['actions']['']
                    ['objects']
                )
                ) {
                    $rights = $this->_rights['']
                                     ['actions']['']
                                     ['objects'][''] ;
                    $canExecute = $rights[$rightName] ;
                }
                if (!$canExecute
                    && array_key_exists(
                        $object,
                        $this->_rights['']
                        ['actions']['']
                        ['objects']
                    )
                ) {
                    $rights = $this->_rights['']
                              ['actions']['']
                              ['objects'][$object] ;
                    $canExecute = $rights[$rightName] ;
                }
            }
            
            if (!$canExecute
                && array_key_exists(
                    $actionName,
                    $this->_rights['']['actions']
                )
            ) {
                if (array_key_exists(
                    '',
                    $this->_rights['']
                    ['actions'][$actionName]
                    ['objects']
                )
                ) {
                    $rights = $this->_rights['']
                                     ['actions'][$actionName]
                                     ['objects'][''] ;
                    $canExecute = $rights[$rightName] ;
                }
                if (!$canExecute
                    && array_key_exists(
                        $object,
                        $this->_rights['']
                        ['actions'][$actionName]
                        ['objects']
                    )
                ) {
                    $rights = $this->_rights['']
                                     ['actions'][$actionName]
                                     ['objects'][$object] ;
                    $canExecute = $rights[$rightName] ;
                }
            }
        }

        if (!$canExecute
            && !is_null($app)
            && array_key_exists(
                $appName,
                $this->_rights
            )
        ) {
            $appName = $app->getName();
            if (array_key_exists(
                '',
                $this->_rights[$appName]['actions']
            )
            ) {
                if (array_key_exists(
                    '',
                    $this->_rights[$appName]
                    ['actions']['']
                    ['objects']
                )
                ) {
                    $rights = $this->_rights[$appName]
                                     ['actions']['']
                                     ['objects'][''] ;
                    $canExecute = $rights[$rightName] ;
                }
                if (!$canExecute
                    && array_key_exists(
                        $object,
                        $this->_rights[$appName]
                        ['actions']['']
                        ['objects']
                    )
                ) {
                    $rights = $this->_rights[$appName]
                                     ['actions']['']
                                     ['objects'][$object] ;
                    $canExecute = $rights[$rightName] ;
                }
            }
            if (!$canExecute
                && array_key_exists(
                    $actionName,
                    $this->_rights[$appName]['actions']
                )
            ) {
                if (array_key_exists(
                    '',
                    $this->_rights[$appName]
                    ['actions'][$actionName]
                    ['objects']
                )
                ) {
                    $rights = $this->_rights[$appName]
                                     ['actions'][$actionName]
                                     ['objects'][''] ;
                    $canExecute = $rights[$rightName] ;
                }
                if (!$canExecute
                    && array_key_exists(
                        $object,
                        $this->_rights[$appName]
                        ['actions'][$actionName]
                        ['objects']
                    )
                ) {
                    $rights = $this->_rights[$appName]
                                     ['actions'][$actionName]
                                     ['objects'][$object] ;
                    $canExecute = $rights[$rightName] ;
                }
            }
        }

        self::$_logger->debug(
            "Object (".$object.") security check : " .
            (($canExecute)?'true':'false')
        );

        return $canExecute ;
    }

    public function getRights()
    {
        return $this->_rights ;
    }

    public static function reset()
    {
        self::$_singleton = null ;
        Session::getInstance()->set('security', null);
    }

    public static function getInstance()
    {

        self::$_logger = new Logger('SecurityManager');

        if (is_null(self::$_singleton)) {
            $session = Session::getInstance();
        
            if (!is_null($session->get('security'))) {
            	self::$_logger->debug('Retrieve security parameters from session');
            	self::$_singleton = $session->get('security');
            } else {
                self::$_logger->debug('Creating new security parameters');
                $app = ApplicationManager::getInstance()->getApplication();
                $user = AuthenticationManager::getInstance()->getAuthenticatedUser();
                self::$_singleton = new SecurityManager($user);
                $session->set('security', self::$_singleton);
            }
        }

        return self::$_singleton;
    }

}
